#lang racket
(require rackunit)
(require racket/generic)
(provide (all-defined-out))

;; Values
(define (r:value? v) (r:number? v))
;; Expressions
(define (r:expression? e) (or (r:value? e) (r:variable? e) (r:apply? e)))


; We are defining function (r:quote x) to dynamically dispatch on x
(define-generics quotable
   (r:quote quotable))

(struct r:number (value) #:transparent
  #:methods gen:quotable
  [
    (define (r:quote n) (r:number-value n))
  ])

(check-equal? (r:quote (r:number 10)) 10)


(struct r:variable (value) #:transparent
  #:methods gen:quotable
  [(define (r:quote n) (r:variable-value n))])

(check-equal? (r:quote (r:variable 'x)) 'x)

; (r:quote (r:variable 'foo))


(struct r:bool (value) #:transparent
  #:methods gen:quotable
  [
    (define (r:quote n) (r:bool-value n))
  ])



(struct r:apply (func args) #:transparent
  #:methods gen:quotable
  [
    (define/generic rec-quote r:quote)
    (define (r:quote app)
      (cons (rec-quote (r:apply-func app))
            (map rec-quote (r:apply-args app))))])

(check-equal?
  (r:quote
    (r:apply (r:variable 'foo)
             (list (r:number 10) (r:variable 'x))))
  '(foo 10 x))

(check-equal?
  (r:quote
    (r:apply (r:variable 'and)
             (list (r:bool #t) (r:bool #f))))
  '(and #t #f))

#|
  (r:quote
    (r:apply (r:variable 'or)
             (list (r:bool #f) (r:bool #f))))
|#
#|
  (r:quote
    (r:apply (r:variable 'foo)
             (list (r:number 10) (r:variable 'x))))
|#















;